#pragma once

#include "XiInclude.hpp"

#define XI_NODE_MAKE_MAX_PARAM_COUNT 10

XI_NAMESPACE_BEGIN

struct XiNode;
typedef boost::shared_ptr<XiNode> XiNodePtr;
typedef boost::weak_ptr<XiNode> XiNodeWPtr;
typedef std::list<XiNodePtr> XiNodeList;

struct XiNode : XiToken, boost::enable_shared_from_this<XiNode>
{
	XiNodeWPtr m_parent;
	XiNodeList m_children;

	static XiNodePtr Make(XiTokenKind kind)
	{
		return XiNodePtr(new XiNode(kind));
	}

	static XiNodePtr Make(const XiToken &token)
	{
		return XiNodePtr(new XiNode(token));
	}

	static XiNodePtr Make(XiNodePtr node)
	{
		return node;
	}

#define MACRO1(z, n, data) node->AddChild(BOOST_PP_CAT(arg, n));

#define MACRO0(z, n, data) \
	template<BOOST_PP_ENUM_PARAMS(n, typename T)> \
	static XiNodePtr Make(BOOST_PP_ENUM_BINARY_PARAMS(n, T, arg)) \
	{ \
		XiNodePtr node = Make(arg0); \
		BOOST_PP_CAT(BOOST_PP_REPEAT_FROM_TO_, z)(1, n, MACRO1, _) \
		return node; \
	};

	BOOST_PP_REPEAT_FROM_TO(2, BOOST_PP_ADD(XI_NODE_MAKE_MAX_PARAM_COUNT, 2), MACRO0, _)

#undef MACRO0
#undef MACRO1

	void Print(int level = 0)
	{
		if (level > 0)
		{
			for (int i = 1; i < level; i++)
				printf("|   ");
			printf("+---");
		}
		wprintf(L"[%s]<%s>(%d, %d)\n", XiGetTokenKindName(m_kind).c_str(), m_text.c_str(), m_line, m_column);

		for (XiNodeList::iterator iter = m_children.begin(); iter != m_children.end(); iter++)
			(*iter)->Print(level + 1);
	}

private:
	explicit XiNode(XiTokenKind kind)
		: XiToken(kind)
	{}

	explicit XiNode(const XiToken &token)
		: XiToken(token)
	{}
private:
	void AddChild(XiTokenKind kind)
	{
		AddChild(XiNodePtr(new XiNode(kind)));
	}

	void AddChild(XiNodePtr node)
	{
		if (node == NULL)
			return;

		XiNodePtr parentOfNode = node->m_parent.lock();
		assert(parentOfNode == NULL);
		node->m_parent = shared_from_this();
		m_children.push_back(node);		
	}

	void AddChild(XiNodeList &nodes)
	{
		while (!nodes.empty())
		{
			XiNodePtr node = nodes.front();
			nodes.pop_front();
			node->m_parent = XiNodePtr();
			AddChild(node);
		}
	}
};

XI_NAMESPACE_END